#include <iostream>
#include <stdio.h>
#include <errno.h>

#include <sys/types.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <arpa/inet.h>

#include <time.h>
#include <pthread.h>
#include <sys/time.h>

bool wait_response = false;
int repeat_times = 10;

double request_time = 0.0; 
double process_time = 0.0; 
double response_time = 0.0; 
int sent_count = 0;
int recved_count = 0;
int error_count = 0;


typedef struct thread_tag{
	pthread_t pid_send;
	pthread_t pid_recv;
	char* host;
	size_t port;
	int sockfd;
	char* msg;

    double request_time;
    double response_time;
    double process_time; 
    int    sent_count;
    int    recved_count;
    int    error_count;
}thread_t;



//client cpp
/*--------------------------------------
 --------------------------------------*/
int connect_server(char* host, size_t port){
	int sockfd = -1;
	struct sockaddr_in srv_addr;
	socklen_t srv_addr_length = sizeof(srv_addr);

	bzero(&srv_addr,sizeof(struct sockaddr_in));
	srv_addr.sin_family = AF_INET;
	srv_addr.sin_port = htons(port);
	if(inet_aton(host, &srv_addr.sin_addr) == 0){
		perror("Server IP Address Error!");
		return -2;
	}

	if(-1 == (sockfd = socket(AF_INET, SOCK_STREAM, 0)) ){
		perror("sock fail!");
		return -1;
	}

	if(connect(sockfd,(struct sockaddr*)&srv_addr, srv_addr_length) < 0) {
		perror("Can Not Connect To!");
		return -3;
	}

	return sockfd;
}

static double get_current_sec()
{//{{{
    struct timeval tv;
    gettimeofday( &tv, NULL );
    return tv.tv_sec + (double)tv.tv_usec/1000000.0f;
}//}}}

/*--------------------------------------
 --------------------------------------*/
void* thread_worker(void*argv){
	thread_t *thread = (thread_t*)argv;

	int sockfd = connect_server(thread->host, thread->port);
	if(sockfd < 0){
		perror("connect fail!");
		return NULL;
	}

	char buf[4096]={'0'};
	std::string _str(1500,'x');
	sprintf(buf, "%d\t\t%d\t%s\t%s\r\n", static_cast<unsigned int>(thread->pid_send), static_cast<int>(time(NULL)), thread->msg, _str.c_str());
	

	for(int i=0;i<repeat_times;++i){
		double start_time = get_current_sec();
		send(sockfd,buf,strlen(buf),0);
		//close(sockfd);
		double stop_time = get_current_sec();
		thread->request_time += stop_time - start_time;
		thread->sent_count++;

		start_time = stop_time;
		if (wait_response) {
			char buffer[65535] = {0};
			int ret = recv(sockfd, buffer, sizeof(buffer), 0);
			if (ret > 0) {
				double timeuse = get_current_sec() - start_time;
				thread->response_time += timeuse;
				thread->recved_count++;
			}
		}
	}

	close(sockfd);

	return NULL;
}

/*--------------------------------------
 --------------------------------------*/
int main(int argc, char** argv){
	wait_response = false;

	int n_threads = atoi(argv[1]);
	repeat_times = atoi(argv[5]);

	thread_t *threads = (thread_t*)calloc(n_threads, sizeof(thread_t));
	for(int i=0; i<n_threads; ++i){
		threads[i].host = argv[2];
		threads[i].port = atoi(argv[3]);
		threads[i].msg = argv[4];
		pthread_create(&threads[i].pid_send, NULL, thread_worker, &threads[i]);
	}

	for(int i=0; i<n_threads; ++i){
		pthread_join(threads[i].pid_send, NULL);
	}

    for (int i = 0; i < n_threads; i++) {
        request_time += threads[i].request_time;
        recved_count += threads[i].recved_count;
        response_time += threads[i].response_time;
        process_time += threads[i].process_time;
        error_count += threads[i].error_count;
        sent_count += threads[i].sent_count;
    }

    int total_count = sent_count;
    int packet_loss_count = total_count - recved_count;
    printf("            total_send_count: %d\n", total_count);
    printf("            total_recv_count: %d\n", recved_count);
    printf("                  lost_count: %d\n", packet_loss_count);
    printf("     send_total_cost: %f s\n", request_time / n_threads);
    printf("    recv_total_cost: %f s\n", response_time / n_threads);
    printf("   average_send_cost: %f s\n", request_time / total_count);
    printf("  average_recv_cost: %f s\n", response_time / recved_count);
    printf("                         qps: %f\n", recved_count / (response_time / n_threads));
    printf("                        ~qps: %f\n", 1 / ((response_time / n_threads) / recved_count));
    printf("                 error_count: %d\n", error_count);

    free(threads);
	return 0;
}
